module qtrt

/*
#include <stdint.h>
#include <ffi.h>

extern void ffi_call_ex(void*fn, int retype, uint64_t* retval, int argc, uint8_t* argtys, uint64_t* argvals);
extern void ffi_call_ex3(void* fn, void* retype, uint64_t* retval,
                  int argc, uintptr_t* argtys, uintptr_t* argvals);
extern void ffi_call_ex3_asmcc(); // just function name is fine, ignore parameters

extern ffi_closure*
make_cppvm_ffi_closure(ffi_cif* cif, void** closfnaddr, void* capdata,
                       void* retype, int argc, uintptr_t* argtys);
extern void cppvm_ffi_closure_callback(void* cthis, void** argvals);
extern void test_call_empty_closure(void* closfn);
*/

/*
import "C"
import (
	"log"
	"reflect"
	"runtime"

	"github.com/kitech/dl/asmcgocall"
	dl "github.com/kitech/dl/dl2"
)

// Library is a dl-opened library holding the corresponding dl.Handle
type FFILibrary struct {
	name   string
	handle dl.Handle
}

// NewFFILibrary takes the library filename and returns a handle towards it.
func NewFFILibrary(libname string) (lib FFILibrary, err error) {
	//libname = get_lib_arch_name(libname)
	lib.name = libname
	lib.handle, err = dl.Open(libname, dl.Now)
	return
}

func (lib FFILibrary) Name() string { return lib.name }
func (lib FFILibrary) Close() error {
	return lib.handle.Close()
}

func (lib FFILibrary) Symbol(fctname string) (Voidptr, error) {
	//println("Fct(",fctname,")...")
	sym, err := lib.handle.Symbol(fctname)
	if err != nil {
		return nil, err
	}

	addr := Voidptr(sym)
	return addr, nil
}

*/

import ffi

[typedef]
struct C.ffi_cif {}

[typedef]
struct C.ffi_closure{
    cif &C.ffi_cif
    fun voidptr
    user_data voidptr
}


/// above indeed just libdl wrapper
// also as callback data
struct CppvmClosure {
    mut:
	// callback for ffi
	cbfn    voidptr // must first
	retype  voidptr
	retval  u64 // C.uint64_t
	argc    int       // = len(argtys)
	argtys  []voidptr // go pointer to c error
	argtys2 size_t // uintptr   // convert and save here
	clsname string
	mthname string
    cpparg_types []string

	upcbfn voidptr // interface{} // go scope function

	cif         C.ffi_cif
	clos        &C.ffi_closure = vnil
	bound_fnptr voidptr
}

fn (this &CppvmClosure) getCif() voidptr      {
    return voidptr(this.clos.cif)
}
fn (this &CppvmClosure) getOrigFunc() voidptr { return voidptr(this.clos.fun) }
fn (this &CppvmClosure) getUserData() voidptr { return this.clos.user_data }
fn (this &CppvmClosure) getFunc() voidptr     { return this.bound_fnptr }

// TODO
/*
fn (this &CppvmClosure) Call(args ...interface{}) {
	if false {
		C.test_call_empty_closure(this.bound_fnptr)
		return
	}
	if true {
		var retval C.uint64_t = 1230
		argvals := []Voidptr{Voidptr(&retval), Voidptr(&retval)}
		log.Println("argaddr", Voidptr(&retval), this.bound_fnptr)
		if this.argc != len(argvals) {
			log.Panicln("only support 2 arg for test")
		}
		C.ffi_call_ex3(this.bound_fnptr, FFITO_POINTER, &retval,
			C.int(this.argc), //
			(*C.uintptr_t)((Voidptr(this.argtys2))),
			(*C.uintptr_t)((Voidptr(&argvals[0]))))
		log.Println("hhh")
	}

	// would callback to go, cannot use asmcgocall
	if false {
		var retval C.uint64_t = 0
		var argv = struct {
			addr    Voidptr
			retype  Voidptr
			retval  *C.uint64_t
			argc    C.int
			argtys  *C.uintptr_t
			argvals *C.uintptr_t
		}{this.bound_fnptr, FFITO_VOID, &retval, C.int(0), nil, nil}
		asmcgocall.Asmcc(C.ffi_call_ex3_asmcc, Voidptr(&argv))
	}
}
*/

// deprecated
[export: 'cppvm_ffi_closure_callback']
fn cppvm_ffi_closure_callback(cthis voidptr, argvals &voidptr) {
    infoln(@FILE, @LINE, "hehe $cthis, $argvals")
	mut this := &CppvmClosure(cthis)
    infoln(@FILE, @LINE, "${this.clsname}, ${this.mthname}, ${this.argtys.len}, ${this.upcbfn}")
	callbackHookInherits2(this, argvals)
}

type Tkey = fn(voidptr) int
[export: 'cppvm_ffi_closure_callback2']
fn cppvm_ffi_closure_callback2(cif voidptr, ret voidptr, args &voidptr,
                               capdata voidptr) {
    infoln(@FILE, @LINE, cif, args[0], args[1])
    vmclos := &CppvmClosure(capdata)
    infoln(@FILE, @LINE, voidptr(&vmclos.cif))
    callbackHookInherits2(vmclos, args)

    //keyfnptr := sym_cfunc6(0, "_ZNK9QKeyEvent3keyEv")
    //fnobj := Tkey(keyfnptr)
    //kno := fnobj(*(&voidptr(args[1])))
    //infoln(@FILE, @LINE, cif, kno)
}

fn C.cppvm_ffi_closure_callback() int
fn C.cppvm_ffi_closure_callback2() int
fn C.make_cppvm_ffi_closure() voidptr
fn C.make_cppvm_ffi_closure2() voidptr

// types ["int", "char*" ...]
fn newCppvmClosure(clsname, mthname string, upcbfn voidptr, types []string) &CppvmClosure {
	mut this := &CppvmClosure{}
	this.clsname = clsname
	this.mthname = mthname
	this.upcbfn = upcbfn
    this.cpparg_types = cpptypes2canty(types)

	this.cbfn = voidptr(C.cppvm_ffi_closure_callback)

	// make_cppvm_ffi_closure(ffi_cif* cif, void** closfnaddr, void* capdata,
	// ffi_type* retype, int argc, ffi_type** argtys) {
    mut argtys := []voidptr{len:2, init: ffi.to_pointer}
    argtys = argtys[..0]
	// this + ...
    argtys << ffi.to_pointer
    for x in cpptypes2ffitype(types) { argtys << x }
	// argtys2 := (*C.uintptr_t)(Voidptr(&argtys[0]))
	// this.argtys2 = (uintptr(Voidptr(argtys2)))
    this.argtys = argtys
    this.argc = argtys.len

    this.bound_fnptr = voidptr(C.cppvm_ffi_closure_callback2)
    this.clos = C.make_cppvm_ffi_closure2(&this.cif, &this.bound_fnptr, voidptr(this),
                                         ffi.to_pointer, this.argc, argtys.data)
	// this.clos = C.make_cppvm_ffi_closure(&this.cif, &this.bound_fnptr, Voidptr(this),
	//	FFITO_POINTER, C.int(this.argc), argtys2)
    infoln(@FILE, @LINE, "$clsname, $mthname, ${this.clos}")

	// runtime.SetFinalizer(this, releaseCppvmClosure)
	return this
}

fn cpptypes2ffitype(types []string) []voidptr {
    mut res := []voidptr{len: types.len, init: ffi.to_pointer}

    for idx, typ in types {
        match typ {
            "int", "bool" { res[idx] = ffi.to_int }
            "float" { res[idx] = ffi.to_float }
            "double" { res[idx] = ffi.to_double }
            "long" { res[idx] = ffi.to_sint64 }
            "short" { res[idx] = ffi.to_sint16 }
            "char" { res[idx] = ffi.to_sint8 }
            else {
                infoln(@FILE, @LINE, "wtt $idx, $typ")
                if typ.ends_with("*") {
                } else if typ.ends_with("&") {
                }
            }
        }
    }

    return res
}

fn cpptypes2canty(types []string) []string {
    mut newtys := []string{len:types.len}

    for idx, typ in types {
        if typ.ends_with(" const*") || typ.ends_with(" const&") {
            newtys[idx] = typ.replace(" const", "")
        }else{
            newtys[idx] = typ
        }
    }
    return newtys
}

/*
func goproto2ffitype(fn interface{}) (argtys []Voidptr) {
	fnty := reflect.TypeOf(fn)
	argc := fnty.NumIn()
	log.Println("argc", argc)
	for i := 0; i < argc; i++ {
		argty := fnty.In(i)
		log.Println("arg", i, argty)
		rety := FFITO_POINTER
		switch argty.Kind() {
		case reflect.Ptr:
		case reflect.Int:
			rety = FFITO_INT
		case reflect.Float64:
			rety = FFITO_DOUBLE
		default:
			log.Panicln("unknown", i, argty)
		}
		argtys = append(argtys, rety)
	}
	return
}
*/

fn releaseCppvmClosure(obj voidptr) {

}


